package com.codeboy.mediasoupandroiddemo;

import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.StaggeredGridLayoutManager;

import android.os.Bundle;
import android.util.Log;
import android.view.View;

import com.codeboy.mediasoupandroiddemo.permission.PermissionFragment;
import com.codeboy.mediasoupandroiddemo.request.Request;
import com.codeboy.mediasoupandroiddemo.room.RoomClient;
import com.codeboy.mediasoupandroiddemo.room.RoomListener;
import com.codeboy.mediasoupandroiddemo.socket.EchoSocket;
import com.codeboy.mediasoupandroiddemo.socket.MessageObserver;
import com.codeboy.mediasoupandroiddemo.utils.RandomString;

import org.json.JSONException;
import org.json.JSONObject;
import org.mediasoup.droid.Consumer;
import org.mediasoup.droid.Device;
import org.mediasoup.droid.Logger;
import org.mediasoup.droid.MediasoupClient;
import org.mediasoup.droid.MediasoupException;
import org.webrtc.EglBase;
import org.webrtc.RendererCommon;
import org.webrtc.SurfaceViewRenderer;
import org.webrtc.VideoTrack;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class MainActivity extends AppCompatActivity
        implements MessageObserver.Observer, RoomListener {
    private static final String TAG = "MainActivity";

    private PermissionFragment mPermissionFragment;
    private RoomClient mClient;
    private EchoSocket socket;

    private RecyclerView mRecyclerView;
    private VideoRoomAdapter mRoomAdapter;
    private EglBase mRootEglBase;
    private SurfaceViewRenderer mVideoView;
    private List<RendererData> mSurfaceViewRendererList = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mRootEglBase = EglBase.create();
        addPermissionFragment();
        getSupportFragmentManager().executePendingTransactions();

        connectWebSocket();
    }

    /**
     * 初始化RecyclerView
     */
    private void initRecyclerView() {
        mRecyclerView = findViewById(R.id.rv_room);
        mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager
                (2, StaggeredGridLayoutManager.VERTICAL));
        mRoomAdapter = new VideoRoomAdapter(mSurfaceViewRendererList);
        mRecyclerView.setAdapter(mRoomAdapter);
    }


    private void connectWebSocket() {
        socket = new EchoSocket();
        socket.register(this);

        try {
            String url = "wss://192.168.11.18:4443?roomId=10086&peerId=" + RandomString.length(7);
            socket.connect(url).get(3000, TimeUnit.SECONDS);

            initializeMediasoupClient();
            JSONObject getRoomRtpCapabilitiesResponse = Request.sendGetRouterRtpCapabilities(socket);
            Log.i("getRoo", getRoomRtpCapabilitiesResponse.toString());

            // Initialize mediasoup device
            Device device = new Device();
            device.load(getRoomRtpCapabilitiesResponse.toString());


            // Create a new room client
            mClient = new RoomClient(socket, device, this);


            // Create recv WebRtcTransport
            mClient.createRecvTransport();

            // Create send WebRtcTransport
            mClient.createSendTransport();

            mClient.join();
            displayLocalVideo();


        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void displayLocalVideo() {
        mVideoView = new SurfaceViewRenderer(this);
        mPermissionFragment.setPermissionCallback(new PermissionFragment.PermissionCallback() {

            @Override
            public void onPermissionGranted() {
                EglBase.Context context = EglBase.create().getEglBaseContext();
                runOnUiThread(() -> mVideoView.init(context, null));
                try {
                    mClient.produceAudio();
                    mClient.produceVideo(getBaseContext(), mVideoView, context);
                    RendererData data = new RendererData();
                    data.id = "0";
                    data.viewRenderer = mVideoView;
                    mSurfaceViewRendererList.add(data);
                    initRecyclerView();
                } catch (MediasoupException e) {
                    e.printStackTrace();
                } catch (Exception e) {
                    e.printStackTrace();
                }

            }

            @Override
            public void onPermissionDenied() {

            }
        });
        mPermissionFragment.checkCameraMicPermission();
    }


    private void initializeMediasoupClient() {
        MediasoupClient.initialize(getApplicationContext());
        Log.d(TAG, "Mediasoup client initialized");

        // Set mediasoup log
        Logger.setLogLevel(Logger.LogLevel.LOG_TRACE);
        Logger.setDefaultHandler();

    }


    @Override
    public void on(String method, int id, boolean notification, JSONObject data) {
        if (method.equals("newConsumer")) {
            handleNewConsumerEvent(data);
        } else if (method.equals("consumerClosed")) {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0;i < mSurfaceViewRendererList.size();i ++) {
                        RendererData data1 = mSurfaceViewRendererList.get(i);
                        try {
                            if (data1.id.equals(data.getString("consumerId")) ){
                                mSurfaceViewRendererList.remove(data1);
                                if (mRoomAdapter != null) {
                                    mRoomAdapter.notifyDataSetChanged();
                                }
                                break;

                            }
                        } catch (JSONException e) {
                            e.printStackTrace();
                        }

                    }
                }
            });
        }
    }


    private void addPermissionFragment() {
        mPermissionFragment = (PermissionFragment) getSupportFragmentManager().findFragmentByTag(PermissionFragment.TAG);

        if (mPermissionFragment == null) {
            mPermissionFragment = PermissionFragment.newInstance();
            getSupportFragmentManager()
                    .beginTransaction()
                    .add(mPermissionFragment, PermissionFragment.TAG)
                    .commit();
        }
    }

    SurfaceViewRenderer createRemoteView() {
        SurfaceViewRenderer remoteView = new SurfaceViewRenderer(this);
        remoteView.init(mRootEglBase.getEglBaseContext(), null); // 初始化
        remoteView.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FILL); // 填充方式
        remoteView.setMirror(true);// 镜面显示
        remoteView.setEnableHardwareScaler(true);
        remoteView.setVisibility(View.VISIBLE);
        return remoteView;

    }

    private void handleNewConsumerEvent(JSONObject consumerInfo) {
        try {
            Log.d(TAG, "handleNewConsumerEvent info =" + consumerInfo);
            mClient.consumeTrack(consumerInfo);
        } catch (Exception e) {
            Log.e(TAG, "Failed to consume remote track", e);
        }
    }

    @Override
    public void onNewConsumer(Consumer consumer) {
        if (consumer != null && consumer.getKind().equals("video")) {
            VideoTrack videoTrack = (VideoTrack) consumer.getTrack();
            videoTrack.setEnabled(true);
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    SurfaceViewRenderer remoteView = createRemoteView();
                    videoTrack.addSink(remoteView);
                    RendererData data = new RendererData();
                    data.id = consumer.getId();
                    data.viewRenderer = remoteView;
                    mSurfaceViewRendererList.add(data);
                    if (mRoomAdapter != null) {
                        mRoomAdapter.notifyDataSetChanged();
                    }
                }
            });
        }

        try {
            mClient.resumeRemoteConsumer(consumer.getId());
        } catch (JSONException je) {
            Log.e(TAG, "Failed to send resume consumer request", je);
        }
    }


}
